package org.sample.commons.lang;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.sample.commons.io.JsonUtils;

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JWTUtils {

    private static volatile Algorithm algorithm;

    static {
        initial(String.valueOf(JWTUtils.class.getName().hashCode()));
    }

    public static void initial(String secret) {
        try {
            algorithm = Algorithm.HMAC256(secret);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    private static Map<String, Object> createHead() {
        Map<String, Object> map = new HashMap<>();
        map.put("typ", "JWT");
        map.put("alg", "HS256");
        return map;
    }

    /**
     * @param obj
     * @param expiry 有效期(秒)
     * @return Token
     * @throws Exception
     */
    public static <T> String sign(T obj, int expiry) {
        JWTCreator.Builder builder = JWT.create();

        builder.withHeader(createHead())
                .withSubject(JsonUtils.toJson(obj));

        if (expiry >= 0) {
            long expMillis = System.currentTimeMillis() + expiry * 1000;
            Date exp = new Date(expMillis);
            builder.withExpiresAt(exp);
        }
        return builder.sign(algorithm);

    }

    public static <T> T unsign(String token, Class<T> clazz) {
        JWTVerifier verifier = JWT.require(algorithm).build();
        DecodedJWT jwt;
        try {
            jwt = verifier.verify(token);
        } catch (JWTVerificationException e) {
            return null;
        }

        Date exp = jwt.getExpiresAt();
        if (exp != null && exp.after(new Date())) {
            String subject = jwt.getSubject();
            return JsonUtils.toObj(subject, clazz);
        }
        return null;
    }
}